package inferenceengine;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;

import ruleengine.RuleEngineManager;

import commondata.IPlayer;
import commondata.ITradeKnowledge;
import commondata.ITradePackage;
import commondata.ITradeRule;
import commondata.ITradeSearch;
import commondata.NbaPool;
import commondata.TradeAttributesEnum;
import commondata.TradeGoalEnum;
import commondata.TradeOptionStruct;
import commondata.TradePackage;

public class TradeSearch implements ITradeSearch {

	private static final int ADVICE_SIZE = 50;

	private static final double AGE_THRESHOLD = 25;

	private NbaPool instance;

	private ITradeKnowledge tradeKnowledge = new TradeKnowledge();

	private ITradeRule tradeRule = new RuleEngineManager();

	private List<ITradePackage> tradePackages;

	private TreeMap<Double, ITradePackage> possibleTradeMap;
	

	public TradeSearch() {

		instance = NbaPool.getInstance();
		tradePackages = instance.getTradePackages();
		possibleTradeMap = new TreeMap<Double, ITradePackage>();
	}

	public List<ITradePackage> findPossibleTrades(TradePackage outgoingTrade,
			List<TradeAttributesEnum> attributes, TradeGoalEnum goalEnum,boolean validationRequired) {

		possibleTradeMap.clear();
		double gain;
		for (ITradePackage possibleTrade : tradePackages) {
			if (goalEnum.equals(TradeGoalEnum.TEAM_FOR_NEXT_SEASON)) {
				if (!checkPlayerAges(possibleTrade)) {
					continue;
				}
			}
			if(checkPositions(outgoingTrade,possibleTrade)){
				gain = calculateGain(outgoingTrade, possibleTrade, attributes);
				managePossibleTrades(gain, outgoingTrade, possibleTrade,validationRequired);
			}
		}
		return getResult(possibleTradeMap.descendingMap().values());
	}

	private boolean checkPositions(TradePackage outgoingTrade,
			ITradePackage possibleTrade) {
		
		for(IPlayer player : possibleTrade.getPlayerList()){
				String pos = player.getPos();
				for(IPlayer out : outgoingTrade.getPlayerList()){
					if(out.getPos().equalsIgnoreCase(pos)){
						return true;
					}
				}
		}
		return false;
	}

	private List<ITradePackage> getResult(Collection<ITradePackage> values) {
		List<ITradePackage> result = new ArrayList<ITradePackage>();
		for(ITradePackage pac: values){
			result.add(pac);
		}
		return result;
	}

	private boolean checkPlayerAges(ITradePackage possibleTrade) {

		double ageAve = 0;
		for (IPlayer player : possibleTrade.getPlayerList()) {
			ageAve += player.getAge();
		}
		ageAve = ageAve / possibleTrade.getPlayerList().size();
		return ageAve < AGE_THRESHOLD;
	}

	private void managePossibleTrades(double gain, ITradePackage outgoingTrade, ITradePackage possibleTrade,boolean validationRequired) {
		if (gain <= 0)
			return;
		if(validationRequired){
			TradeOptionStruct struct = makeTradeOptionStruct(outgoingTrade,
					possibleTrade);
			tradeRule.isTradeApplicable(struct);
			if(struct.getRuleEngineResults().size()> 0)
				return;
		}
		if (possibleTradeMap.size() < ADVICE_SIZE) {
			possibleTradeMap.put(gain, possibleTrade);
		} else {
			double minGain = possibleTradeMap.firstKey();
			if (gain > minGain) {
				possibleTradeMap.remove(minGain);
				possibleTradeMap.put(gain, possibleTrade);
			}

		}
	}

	private double calculateGain(TradePackage outgoingTrade,
			ITradePackage possibleTrade, List<TradeAttributesEnum> attributes) {
		double gain = 0;

		for (TradeAttributesEnum attribute : attributes) {
			gain += calculateGain(outgoingTrade, possibleTrade, attribute);
		}

		return gain;
	}

	private double calculateGain(TradePackage outgoingTrade,
			ITradePackage possibleTrade, TradeAttributesEnum attribute) {

		if (attribute.equals(TradeAttributesEnum.Point)) {
			return getPercentage(possibleTrade.getPointPG(), outgoingTrade
					.getPointPG());
		} else if (attribute.equals(TradeAttributesEnum.Rebound)) {
			return getPercentage(possibleTrade.getReboundPG(), outgoingTrade
					.getReboundPG());
		} else if (attribute.equals(TradeAttributesEnum.Assist)) {
			return getPercentage(possibleTrade.getAssistPG(), outgoingTrade
					.getAssistPG());
		} else if (attribute.equals(TradeAttributesEnum.Block)) {
			return getPercentage(possibleTrade.getBlockPG(), outgoingTrade
					.getBlockPG());
		} else {
			return getPercentage(possibleTrade.getStealPG(), outgoingTrade
					.getStealPG());
		}
	}

	private double getPercentage(double inPG, double outPG) {
		double diff = inPG - outPG;
		if (diff == 0)
			return 0;

		if (outPG == 0)
			return 100;

		return diff * 100 / outPG;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see commondata.ITradeSearch#findPossibleOptions(commondata.TradePackage,
	 *      java.util.List, commondata.TradeGoalEnum)
	 */
	public List<TradeOptionStruct> findPossibleOptions(
			TradePackage outgoingTrade, List<TradeAttributesEnum> attributes,
			TradeGoalEnum goalEnum, boolean validationRequired) {

		List<TradeOptionStruct> result = new ArrayList<TradeOptionStruct>();
		List<ITradePackage> trades = findPossibleTrades(outgoingTrade,
				attributes, goalEnum,validationRequired);
		for (ITradePackage trade : trades) {
			TradeOptionStruct struct = makeTradeOptionStruct(outgoingTrade,
					trade);
			tradeRule.isTradeApplicable(struct);
			struct.getKnowledgeInferenceResults().add(
					tradeKnowledge.getTradeGainForFirstTeam(struct));
			result.add(struct);
		}
		return result;
	}

	private TradeOptionStruct makeTradeOptionStruct(ITradePackage outgoingTrade,
			ITradePackage trade) {
		TradeOptionStruct struct = new TradeOptionStruct();
		struct.setFirstTeamPlayers(outgoingTrade.getPlayerList());
		struct.setFirstTeam(instance.getNbaTeams().get(
				outgoingTrade.getTeamID()));
		struct.setSecondTeamPlayers(trade.getPlayerList());
		struct.setSecondTeam(instance.getNbaTeams().get(trade.getTeamID()));
		return struct;
	}
}
